/*-
 * Copyright 2012 Guram Dukashvili
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
//---------------------------------------------------------------------------
#include "sys.h"
#include <wchar.h>
#include "str.h"
//---------------------------------------------------------------------------
static char null[] = "";
//---------------------------------------------------------------------------
string * string_new(void)
{
  string * s = salloc(sizeof(string),NULL);
  if( s != NULL )
    string_init(s);
  return s;
}
//---------------------------------------------------------------------------
void string_delete(string * s)
{
  string_destroy(s);
  sfree(s);
}
//---------------------------------------------------------------------------
void string_destroy(string * s)
{
  if( s != NULL && s->s != null )
    sfree(s->s);
}
//---------------------------------------------------------------------------
string * string_new_copy(const char * src,uintptr_t l)
{
  struct string * s = string_new();
  if( s != NULL )
    string_copy_c(s,src,l);
  return s;
}
//---------------------------------------------------------------------------
void string_init(string * s)
{
  s->s = null;
}
//---------------------------------------------------------------------------
void string_copy_c(string * s,const char * src,uintptr_t l)
{
  uintptr_t n = l > 0 ? l : strlen(src);
  char * a = salloc(n + 1,NULL);
  if( a != NULL ){
    memcpy(a,src,n);
    a[n] = '\0';
    if( s->s != null ) sfree(s->s);
    s->s = a;
  }
}
//---------------------------------------------------------------------------
void string_copy(string * d,const string * s)
{
  string_copy_c(d,s->s,0);
}
//---------------------------------------------------------------------------
void string_cat_c(string * d,const string * s1,const char * s2,uintptr_t l)
{
  uintptr_t l1 = strlen(s1->s);
  uintptr_t l2 = l > 0 ? l : strlen(s2);
  char * a = salloc(l1 + l2 + 1,NULL);
  if( a != NULL ){
    memcpy(a,s1->s,l1);
    memcpy(a + l1,s2,l2);
    a[l1 + l2] = '\0';
    if( d->s != null ) sfree(d->s);
    d->s = a;
  }
}
//---------------------------------------------------------------------------
void string_cat(string * d,const string * s1,const string * s2)
{
  string_cat_c(d,s1,s2->s,0);
}
//---------------------------------------------------------------------------
void string_vcat_c(string * d, /* const char * s1 */ ...)
{
  char * a;
  uintptr_t l = 0, i;
  va_list ap;

  va_start(ap,d);

  for(;;){
    const char * s = va_arg(ap,const char *);
    if( s == NULL ) break;
    l += strlen(s);
  }

  va_end(ap);

  a = salloc(l + 1,NULL);
  if( a != NULL ){
    va_start(ap,d);

    i = 0;

    for(;;){
      const char * s = va_arg(ap,const char *);
      if( s == NULL ) break;
      l = strlen(s);
      memcpy(a + i,s,l);
      i += l;
    }

    a[i] = '\0';

    va_end(ap);

    if( d->s != null ) sfree(d->s);
    d->s = a;

  }
}
//---------------------------------------------------------------------------
void string_vcat(string * d, /* const string * s1 */ ...)
{
  uintptr_t l = 0, i;
  char * a;
  va_list ap;

  va_start(ap,d);

  for(;;){
    const string * s = va_arg(ap,const string *);
    if( s == NULL ) break;
    l += strlen(s->s);
  }

  va_end(ap);

  a = salloc(l + 1,NULL);
  if( a != NULL ){
    va_start(ap,d);

    i = 0;

    for(;;){
      const string * s = va_arg(ap,const string *);
      if( s == NULL ) break;
      l = strlen(s->s);
      memcpy(a + i,s->s,l);
      i += l;
    }

    a[i] = '\0';

    va_end(ap);

    if( d->s != null ) sfree(d->s);
    d->s = a;

  }
}
//---------------------------------------------------------------------------
uintptr_t string_length(const string * s)
{
  uintptr_t l = 0;
  char * p = s->s;
  size_t char_len;
  mbstate_t state;

  memset(&state,0,sizeof(state));

  while( (char_len = mbrlen(p,MB_CUR_MAX,&state)) != 0 &&
                    char_len != (size_t) -1 && char_len != (size_t) -2 ){
    s += char_len;
    l++;
  }

  return l;
}
//---------------------------------------------------------------------------
uintptr_t string_size(const string * s)
{
  return strlen(s->s);
}
////---------------------------------------------------------------------------
void string_vprint(string * s,const char * fmt,va_list args)
{
#ifndef _MSC_VER
  va_list argsw
#if __GCC_VER__ > 421
   = args
#endif
  ;
#endif
  int err = 0, n = 16, l;
  char * p = NULL, * a;

  for(;;){
    sfree(p);
    a = (char *) salloc(sizeof(char) * (n + 1),NULL);
    err = errno;
    if( a == NULL ) break;
    p = a;
#ifndef _MSC_VER
    memcpy(argsw,args,sizeof(argsw));
#if HAVE__VSNPRINTF
    l = _vsnprintf(p,n,fmt,argsw);
#elif HAVE_VSNPRINTF
    l = vsnprintf(p,n,fmt,argsw);
#endif
#else
#if HAVE__VSNPRINTF
    l = _vsnprintf(p,n,fmt,args);
#elif HAVE_VSNPRINTF
    l = vsnprintf(p,n,fmt,args);
#endif
#endif
    err = errno;
    if( l == -1 && err != ERANGE ) break;
    if( l >= 0 && l <= n ){
      if( l != n ){
        a = salloc(sizeof(char) * (l + 1),NULL);
        err = errno;
        if( a == NULL ) break;
        memcpy(a,p,sizeof(char) * (l + 1));
        sfree(p);
        p = a;
      }
      p[l] = '\0';
      if( s->s != null ) sfree(s->s);
      s->s = p;
      p = NULL;
      break;
    }
    n <<= 1;
  }
  sfree(p);
  errno = err;
}
//---------------------------------------------------------------------------
void string_print(string * s,const char * fmt, ... )
{
  va_list args;
  va_start(args,fmt);
  string_vprint(s,fmt,args);
  va_end(args);
}
//---------------------------------------------------------------------------
const char * string_skip_sym(const char * s,uintptr_t m)
{
  size_t char_len;
  mbstate_t state;

  memset(&state,0,sizeof(state));

  while( m > 0 && (char_len = mbrlen(s,MB_CUR_MAX,&state)) != 0 &&
                        char_len != (size_t) -1 && char_len != (size_t) -2 ){
    s++;
    m--;
  }
  return s;
}
//---------------------------------------------------------------------------
uint32_t string_hash(const string * s,uint32_t h)
{
  const char * a = s->s;
  for(;;){
    if( *a == '\0' ) break;
    h = (h << 5) + h + *a;
    if( *a == '\0' ) break;
    h = (h << 5) + h + *a;
    if( *a == '\0' ) break;
    h = (h << 5) + h + *a;
    if( *a == '\0' ) break;
    h = (h << 5) + h + *a;
    if( *a == '\0' ) break;
    h = (h << 5) + h + *a;
    if( *a == '\0' ) break;
    h = (h << 5) + h + *a;
    if( *a == '\0' ) break;
    h = (h << 5) + h + *a;
    if( *a == '\0' ) break;
    h = (h << 5) + h + *a;
  }
  return h;
}
//---------------------------------------------------------------------------
intptr_t string_compare(const string * s1,const string * s2)
{
  return strcoll(s1->s,s2->s);
}
//---------------------------------------------------------------------------
intptr_t string_icompare(const string * s1,const string * s2)
{
#if HAVE_STRCASECMP
  return strcasecmp(s1->s,s2->s);
#elif HAVE__STRICOLL
  return _stricoll(s1->s,s2->s);
#else
#error must be implemented for arch
#endif
}
//---------------------------------------------------------------------------
void string_xchg(string * s1,string * s2)
{
  char * t = s1->s;
  s1->s = s2->s;
  s2->s = t;
}
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
string_it * string_it_new(string * s)
{
  string_it * it = salloc(sizeof(string_it),NULL);
  if( it != NULL )
    string_it_init(it,s);
  return it;
}
//---------------------------------------------------------------------------
string_it * string_it_new_copy(const string_it * src)
{
  string_it * it = salloc(sizeof(string_it),NULL);
  if( it != NULL )
    string_it_init_copy(it,src);
  return it;
}
//---------------------------------------------------------------------------
void string_it_init(string_it * it,string * s)
{
  it->s = s;
  it->position = it->cursor = 0;
}
//---------------------------------------------------------------------------
void string_it_init_copy(string_it * it,const string_it * src)
{
  it->s = src->s;
  it->position = src->position;
  it->cursor = src->cursor;
}
//---------------------------------------------------------------------------
void string_it_delete(string_it * it)
{
  string_it_destroy(it);
  sfree(it);
}
//---------------------------------------------------------------------------
void string_it_destroy(string_it * it)
{
  it->s = NULL;
  it->position = 0;
  it->cursor = 0;
}
//---------------------------------------------------------------------------
void string_it_prev(string_it * it)
{
  size_t len;
  uintptr_t cursor = it->cursor;
  mbstate_t state;
  memset(&state,0,sizeof(state));

  errno = EILSEQ;

  for(;;){

    if( cursor == 0 ){
      errno = ERANGE;
      break;
    }

    cursor--;
    len = mbrlen(it->s->s + cursor,MB_CUR_MAX,&state);

    if( len != (size_t) -1 && len != (size_t) -2 ){
      it->cursor = cursor;
      it->position--;
      errno = 0;
      break;
    }

    if( it->cursor - cursor > (uintptr_t) MB_CUR_MAX ){
      errno = EILSEQ;
      break;
    }

  }
}
//---------------------------------------------------------------------------
void string_it_next(string_it * it)
{
  size_t len;
  mbstate_t state;

  if( it->s->s[it->cursor] == '\0' ){
    errno = ERANGE;
  }
  else {
    memset(&state,0,sizeof(state));
    len = mbrlen(it->s->s + it->cursor,MB_CUR_MAX,&state);
    if( len != (size_t) -1 && len != (size_t) -2 ){
      it->cursor += len;
      it->position++;
      errno = 0;
    }
    else {
      errno = EILSEQ;
    }
  }
}
//---------------------------------------------------------------------------
void string_it_rewind(string_it * it)
{
  it->position = it->cursor = 0;
}
//---------------------------------------------------------------------------
void string_it_end(string_it * it)
{
  string_it_move_position(it,INTPTR_MAX);
}
//---------------------------------------------------------------------------
void string_it_move_position(string_it * it,intptr_t syms)
{
  while( syms > 0 ){
    string_it_next(it);
    if( errno != 0 ) break;
    syms--;
  }

  while( syms < 0 ){
    string_it_prev(it);
    if( errno != 0 ) break;
    syms++;
  }
}
//---------------------------------------------------------------------------
int string_it_getc(const string_it * it)
{
  wchar_t c;
  size_t len;
  mbstate_t state;
  memset(&state,0,sizeof(state));

  len = mbrtowc(&c,it->s->s + it->cursor,MB_CUR_MAX,&state);
  return len != (size_t) -1 && len != (size_t) -2 ? c : -1;
 }
//---------------------------------------------------------------------------
#if _MSC_VER
void string_ansi2oem(string * s)
{
  LPWSTR lpWideCharStr;
  int sl, wl;

  wl = MultiByteToWideChar(GetACP(),MB_USEGLYPHCHARS,s->s,-1,NULL,0);
  lpWideCharStr = salloc(sizeof(WCHAR) * wl,NULL);
  if( lpWideCharStr != NULL ){
    MultiByteToWideChar(GetACP(),MB_USEGLYPHCHARS,s->s,-1,lpWideCharStr,wl);
    sl = WideCharToMultiByte(GetOEMCP(),WC_NO_BEST_FIT_CHARS,lpWideCharStr,-1,s->s,0,NULL,NULL);
    if( sl <= (int) (strlen(s->s) + 1) )
      WideCharToMultiByte(GetOEMCP(),0,lpWideCharStr,-1,s->s,sl,NULL,NULL);
    sfree(lpWideCharStr);
  }
}
#endif
//---------------------------------------------------------------------------
